﻿// ===============================================================================
//  Copyright © ymind.net. All rights reserved .
//  官方网站：http://ymind.net/
//  版权所有：彦铭工作室 老陈出品
// ===============================================================================

using System;
using System.Reflection;
using System.Reflection.Emit;

namespace Lcsoft.Data.Reflection
{
	/// <summary>
	/// 包含一组静态方法，提供动态编译的功能。
	/// </summary>
	internal static class DynamicMethodCompiler
	{
		private static DynamicMethod _CreateGetDynamicMethod<T>(PropertyInfo property) { return new DynamicMethod(String.Concat("_Get", property.Name, "_"), typeof(object), new[] { typeof(object) }, typeof(T), true); }

		private static DynamicMethod _CreateSetDynamicMethod<T>(string propertyName) { return new DynamicMethod(String.Concat("_Set", propertyName, "_"), typeof(void), new[] { typeof(object), typeof(object) }, typeof(T), true); }

		private static void _BoxIfNeeded(Type type, ILGenerator generator) { if (type.IsValueType) generator.Emit(OpCodes.Box, type); }

		private static void _UnboxIfNeeded(Type type, ILGenerator generator) { if (type.IsValueType) generator.Emit(OpCodes.Unbox_Any, type); }

		/// <summary>
		/// 创建一个可用于执行无参数构造函数的方法的委托。
		/// </summary>
		/// <typeparam name="T">指定一个实例对象的类型。</typeparam>
		/// <returns>返回 <see cref="InstanceCreateHandler"/> 委托。</returns>
		public static InstanceCreateHandler CreateInstance<T>()
		{
			var t = typeof(T);
			var constructor = t.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[0], null);

			if (constructor == null) throw new Exception(String.Format("类型 {0} 必须包含一个无参数构造函数。", t));

			var dynamicMethod = new DynamicMethod("ObjectInstance", MethodAttributes.Static | MethodAttributes.Public, CallingConventions.Standard, typeof(object), null, t, true);

			var generator = dynamicMethod.GetILGenerator();
			generator.Emit(OpCodes.Newobj, constructor);
			generator.Emit(OpCodes.Ret);

			return (InstanceCreateHandler)dynamicMethod.CreateDelegate(typeof(InstanceCreateHandler));
		}

		/// <summary>
		/// 创建一个可用于执行指定的构造函数的方法的委托。
		/// </summary>
		/// <typeparam name="T">指定一个实例对象的类型。</typeparam>
		/// <param name="constructor">表示构造函数的 <see cref="ConstructorInfo"/>。</param>
		/// <returns>返回 <see cref="InstanceCreateHandler"/> 委托。</returns>
		public static InstanceCreateHandler CreateInstance<T>(ConstructorInfo constructor)
		{
			var t = typeof(T);

			if (constructor == null) throw new Exception(String.Format("类型 {0} 必须包含一个无参数构造函数。", t));

			var dynamicMethod = new DynamicMethod("ObjectInstance", MethodAttributes.Static | MethodAttributes.Public, CallingConventions.Standard, typeof(object), null, t, true);

			var generator = dynamicMethod.GetILGenerator();
			generator.Emit(OpCodes.Newobj, constructor);
			generator.Emit(OpCodes.Ret);

			return (InstanceCreateHandler)dynamicMethod.CreateDelegate(typeof(InstanceCreateHandler));
		}

		/// <summary>
		/// 创建一个可用于执行指定结构的方法的委托。
		/// </summary>
		/// <typeparam name="T">指定一个实例对象的类型。</typeparam>
		/// <returns>返回 <see cref="InstanceCreateHandler"/> 委托。</returns>
		public static InstanceCreateHandler CreateStructInstance<T>()
		{
			var t = typeof(T);
			var dynamicMethod = new DynamicMethod("InstantiateStruct", MethodAttributes.Static | MethodAttributes.Public, CallingConventions.Standard, typeof(object), null, t, true) {
				InitLocals = true
			};

			var generator = dynamicMethod.GetILGenerator();
			generator.DeclareLocal(t);
			generator.Emit(OpCodes.Ldloc_0);
			generator.Emit(OpCodes.Box, t);
			generator.Emit(OpCodes.Ret);

			return (InstanceCreateHandler)dynamicMethod.CreateDelegate(typeof(InstanceCreateHandler));
		}

		/// <summary>
		/// 创建一个可用于执行 get 访问器的动态方法的委托。
		/// </summary>
		/// <typeparam name="T">指定一个实例对象的类型。</typeparam>
		/// <param name="propertyInfo">表示属性的 <see cref="PropertyInfo"/>。</param>
		/// <returns>返回 <see cref="GetPropertyHandler"/> 委托。</returns>
		public static GetPropertyHandler CreatePropertyGetHandler<T>(PropertyInfo propertyInfo)
		{
			if (propertyInfo == null) throw new ArgumentNullException("propertyInfo");

			var getMethodInfo = propertyInfo.GetGetMethod();

			if (getMethodInfo == null) return null;

			var dynamicGet = _CreateGetDynamicMethod<T>(propertyInfo);
			var getGenerator = dynamicGet.GetILGenerator();

			getGenerator.Emit(OpCodes.Ldarg_0);
			getGenerator.Emit(OpCodes.Call, getMethodInfo);
			_BoxIfNeeded(getMethodInfo.ReturnType, getGenerator);
			getGenerator.Emit(OpCodes.Ret);

			return (GetPropertyHandler)dynamicGet.CreateDelegate(typeof(GetPropertyHandler));
		}

		/// <summary>
		/// 创建一个可用于执行 set 访问器的动态方法的委托。
		/// </summary>
		/// <typeparam name="T">指定一个实例对象的类型。</typeparam>
		/// <param name="propertyInfo">表示属性的 <see cref="PropertyInfo"/>。</param>
		/// <returns>返回 <see cref="SetPropertyHandler"/> 委托。</returns>
		public static SetPropertyHandler CreateSetPropertyHandler<T>(PropertyInfo propertyInfo)
		{
			if (propertyInfo == null) throw new ArgumentNullException("propertyInfo");

			var setMethodInfo = propertyInfo.GetSetMethod(true);
			var dynamicSet = _CreateSetDynamicMethod<T>(propertyInfo.Name);
			var setGenerator = dynamicSet.GetILGenerator();

			setGenerator.Emit(OpCodes.Ldarg_0);
			setGenerator.Emit(OpCodes.Ldarg_1);
			_UnboxIfNeeded(setMethodInfo.GetParameters()[0].ParameterType, setGenerator);
			setGenerator.Emit(OpCodes.Call, setMethodInfo);
			setGenerator.Emit(OpCodes.Ret);

			return (SetPropertyHandler)dynamicSet.CreateDelegate(typeof(SetPropertyHandler));
		}
	}
}
